"""
PyQt friendly wrapper around the remote registration API client.
"""

from __future__ import annotations

import traceback
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

from PyQt5 import QtCore

from services.api_client import RegistrationAPIClient


class RegistrationClient(QtCore.QObject):
    """
    Asynchronous registration client suitable for wiring into PyQt widgets.
    """

    registrationSucceeded = QtCore.pyqtSignal(dict)
    registrationFailed = QtCore.pyqtSignal(str)

    def __init__(self, base_url: str, parent: Optional[QtCore.QObject] = None) -> None:
        super().__init__(parent)
        self._api_client = RegistrationAPIClient(base_url)

    # ------------------------------------------------------------------ #
    # Synchronous wrapper
    # ------------------------------------------------------------------ #
    def register_point_clouds(
        self,
        source_ply: Path | str,
        target_ply: Path | str,
        *,
        is_small_cloud: Optional[bool] = None,
        max_attempts: Optional[int] = None,
        random_seed: Optional[int] = None,
        timeout_seconds: int = 600,
    ) -> dict:
        return self._api_client.register_point_clouds(
            source_ply,
            target_ply,
            is_small_cloud=is_small_cloud,
            max_attempts=max_attempts,
            random_seed=random_seed,
            timeout_seconds=timeout_seconds,
        )

    # ------------------------------------------------------------------ #
    # Asynchronous API
    # ------------------------------------------------------------------ #
    def trigger_registration(
        self,
        source_ply: Path | str,
        target_ply: Path | str,
        *,
        is_small_cloud: Optional[bool] = None,
        max_attempts: Optional[int] = None,
        random_seed: Optional[int] = None,
        timeout_seconds: int = 600,
    ) -> None:
        worker = _RegistrationWorker(
            api_client=self._api_client,
            source_ply=Path(source_ply),
            target_ply=Path(target_ply),
            is_small_cloud=is_small_cloud,
            max_attempts=max_attempts,
            random_seed=random_seed,
            timeout_seconds=timeout_seconds,
        )
        thread = QtCore.QThread(self)
        worker.moveToThread(thread)

        thread.started.connect(worker.run)  # type: ignore[arg-type]
        worker.finished.connect(thread.quit)
        worker.finished.connect(worker.deleteLater)
        thread.finished.connect(thread.deleteLater)

        worker.registrationSucceeded.connect(self.registrationSucceeded)
        worker.registrationFailed.connect(self.registrationFailed)

        thread.start()


@dataclass
class _WorkerConfig:
    source_ply: Path
    target_ply: Path
    is_small_cloud: Optional[bool]
    max_attempts: Optional[int]
    random_seed: Optional[int]
    timeout_seconds: int


class _RegistrationWorker(QtCore.QObject):
    registrationSucceeded = QtCore.pyqtSignal(dict)
    registrationFailed = QtCore.pyqtSignal(str)
    finished = QtCore.pyqtSignal()

    def __init__(
        self,
        api_client: RegistrationAPIClient,
        source_ply: Path,
        target_ply: Path,
        is_small_cloud: Optional[bool],
        max_attempts: Optional[int],
        random_seed: Optional[int],
        timeout_seconds: int,
    ) -> None:
        super().__init__()
        self._api_client = api_client
        self._config = _WorkerConfig(
            source_ply=source_ply,
            target_ply=target_ply,
            is_small_cloud=is_small_cloud,
            max_attempts=max_attempts,
            random_seed=random_seed,
            timeout_seconds=timeout_seconds,
        )

    @QtCore.pyqtSlot()
    def run(self) -> None:
        try:
            result = self._api_client.register_point_clouds(
                source_ply=self._config.source_ply,
                target_ply=self._config.target_ply,
                is_small_cloud=self._config.is_small_cloud,
                max_attempts=self._config.max_attempts,
                random_seed=self._config.random_seed,
                timeout_seconds=self._config.timeout_seconds,
            )
            self.registrationSucceeded.emit(result)
        except Exception as exc:  # pragma: no cover - network failure path
            tb = traceback.format_exc()
            self.registrationFailed.emit(f"配准失败: {exc}\n{tb}")
        finally:
            self.finished.emit()
